﻿function ContainerSupport(){
    var _oContainerContext=null;
    var _this=this;
    
    this.setContainerContext=function(oContainerContext){
        _oContainerContext=oContainerContext.register(_this);
    }
    
    this.getContainerContext=function(oContainerContext){
        return _oContainerContext;
    }
}

function IContainer(){
    var _oInstances={};
    var _oContainerSupport={};
    var _oContextSupportInstances=[];
    var _oContainerContext=new ContainerContext();
    
    this.load=function(oContainerDefinition){
        _oInstances['containerContext']=new ContainerContextProxy();
        autoWire(oContainerDefinition);
    }
    
    /**
    *   @name   :   autoWire
    *   @desc   :   Takes a container definition as a parameter, creates class instances and wires them by reference
    */
    function autoWire(oContainerDefinition){
        //  Create instances in order of dependancy and wire constructor args
        forEach(oContainerDefinition,function(i,n){
            createInstance(i,n);
        });
        
        //  Set properties
        forEach(oContainerDefinition,function(i,n){
            var oNode=_oInstances[i];
            
            if(n.props!=null){
                forEach(n.props,function(i,n){
                    var methodToInvoke='set'+getMethodName((n.name!=null)?n.name:n.ref);
                    oNode[methodToInvoke](_oInstances[n.ref]);
                });
            }
        });
        
        //  Invoke ContextSupport method on instances that support it
        forEach(_oContextSupportInstances,function(i,n){
            (n.onContextSupport==null)?function(){throw("Interface not supported");}:n.onContextSupport(ContainerContext);
        });
    }
    
    /**
    *   @name   :   createInstance
    *   @para   :   [String]    i   An alphnumeric id used to hash the instance in the instance hash object
    *   @para   :   [Object]    n   The node definition instance
    */
    function createInstance(i,n){
        var _aReferences=new Array();
        
        if(_oInstances[i]==null){
            if(n.args!=null) _aReferences=findReferences(n);
            _oInstances[i]={};
            
            n.type.apply(_oInstances[i],_aReferences);
        }
        
        return _oInstances[i];
    }
    
    /**
    *   @name   :   findReferences
    *   @para   :   [Object]    n   The node definition instance
    */
    function findReferences(n){
        var _aReferences=new Array();
        
        forEach(n.args,function(i,n){
            _aReferences.push(createInstance(n.ref,n));
        });
        
        return _aReferences;
    }
    
    /**
    *   @name   :   ContainerContextProxy
    *   @desc   :   An inner class that encapsulates the Container context and allows instances to 
                    register for Context Support
    */
    function ContainerContextProxy(){
        this.register=function(oObject){
            _oContextSupportInstances.push(oObject);
            return _oContainerContext;
        }
    }
    
    /**
    *   @name   :   ContainerContext
    *   @desc   :   An inner class that encapsulates container context access
    */
    function ContainerContext(){
        //  Get a bean by id
        this.getBean=function(sBeanId){
            return _oInstances[sBeanId];
        }
    }
    
    
    function getMethodName(str){
        return String(str).replace(/\b\w/gi,function(c,i,s){
            return c.toUpperCase();
        });
    }
    
    function forEach(oCollection,fCallback){
        if(isArray(oCollection)){
            for(iIndex=0;iIndex<oCollection.length;iIndex++){
                callBack(iIndex,oCollection[iIndex]);
            }
        }else{
            for(each in oCollection){
                callBack(each,oCollection[each]);
            }
        }
        
        function callBack(i,n){
            try{
                fCallback(i,n);
            }catch(e){
            }
        }
    }
    
    function isArray(obj) {
       return (obj.constructor.toString().indexOf("Array") == -1)?false:true;
    }
}

